#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/stm32f10x_exti.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/misc.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/stm32f10x_gpio.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/stm32f10x_rcc.h"

void uart1_init(void)
{
	// 定义GPIO的初始化信息结构体
	GPIO_InitTypeDef tx,rx;
	// 定义UART的初始化信息结构体
	USART_InitTypeDef usart1;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//打开串口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIO口时钟
	
	//tx pa9
	tx.GPIO_Pin = GPIO_Pin_9;
	// 写引脚（输出）模式为推挽
	// 推挽用于0~3.3v场景，高速切换开关的场景
	// 而开漏则是只要用于匹配电平
	tx.GPIO_Mode = GPIO_Mode_AF_PP; 
	tx.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&tx);//tx初始化
	
	//rx pa10
	tx.GPIO_Pin = GPIO_Pin_10;
	// 读引脚的模式为浮空
	// 浮空输入代表输入情况不定，需要根据外部输入情况来确定，
	// 而上拉和下拉的输入则是输入确定为高电平/ 低电平
	tx.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	GPIO_Init(GPIOA,&rx);//rx初始化
	
	//usart1 参数配置
	//波特率，注意外面的串口工具的波特率要和这里配置一致
	usart1.USART_BaudRate = 9600;
	//串口硬件流设置
	usart1.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	//串口收+发模式使能
	usart1.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	//校验位
	usart1.USART_Parity = USART_Parity_No;
	//停止位
	usart1.USART_StopBits = USART_StopBits_1;
	//字节长度
	usart1.USART_WordLength = USART_WordLength_8b;
	
	USART_Init(USART1,&usart1);//串口参数初始化
	USART_Cmd(USART1,ENABLE);//串口使能
}

void delay(unsigned int time)
{
	unsigned int i = 0;
	while (time--)
	{
		i = 1000000;
		while (i--)
			;
	}
}

u8 key_read()
{
	u8 result = 0;
	// delay(1);
	// 注意这里是读取InputDataBit
	result = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);

	return result;
}

void led_init()
{
	GPIO_InitTypeDef led;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	led.GPIO_Pin = GPIO_Pin_13;
	led.GPIO_Mode = GPIO_Mode_Out_PP;
	led.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &led);
	GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}

void key_init()
{
	GPIO_InitTypeDef key;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	key.GPIO_Pin = GPIO_Pin_12;
	key.GPIO_Mode = GPIO_Mode_IPD; // 因为没事是低电平，所以默认模式是下拉（高电平就是接通状态）
	// led.GPIO_Speed = GPIO_Speed_50MHz; // 不需要设置时钟
	GPIO_Init(GPIOB, &key);
	// GPIO_WriteBit(GPIOB, GPIO_Pin_12, Bit_RESET);
}

void led_opr(int opr)
{
	if (1 == opr)
	{
		GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
	}
	else
	{
		GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
	}
}

void key_exti_init()
{
	// 注意在MDK编译器里面，声明必须要放在最前面，即可执行代码段的前面，否则编译会报错
	// error:  #268: declaration may not appear after executable statement in block
	EXTI_InitTypeDef key_exti;

	// 使能AFIO，为什么要使能AFIOI，参看GPIO_EXTILineConfig里面的实现就知道了
	// 参看芯片手册里面GPIO章节里面AFIO小结里面有介绍，如果想要让引脚相应中断，就需要配置AFIO
	// 对于三个寄存器操作前需要打开AFIO时钟（9.4 AFIO Register里面有介绍）：
	// AFIO_MAPR 	// 配置复用功能重映射
	// AFIO_EXTICRX // 配置外部中断线映射
	// AFIO_EVCR 	// 配置复用功能重映射
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);

	key_exti.EXTI_Line = EXTI_Line12;		  // 指定是12号中断线
	key_exti.EXTI_LineCmd = ENABLE;			  // 使能
	key_exti.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式（而非事件模式）
	key_exti.EXTI_Trigger = EXTI_Trigger_Rising_Falling;

	EXTI_Init(&key_exti);
}

void key_nvid_init()
{
	NVIC_InitTypeDef key_nvic;
	key_nvic.NVIC_IRQChannel = EXTI15_10_IRQn;		// 绑定中断处理函数（在下面有定义）
	key_nvic.NVIC_IRQChannelCmd = ENABLE;			// 使能NVIC中断
	key_nvic.NVIC_IRQChannelPreemptionPriority = 1; // 子通道优先级
	key_nvic.NVIC_IRQChannelSubPriority = 0;		// 父通道优先级

	NVIC_Init(&key_nvic);
}

void uart1_send(const u8 *data)
{
	while(*data)
	{
		USART_ClearFlag(USART1, USART_FLAG_TC);
		USART_SendData(USART1,*data++);
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); //等待数据发送完毕
		//delay_ms(1);
	}
}

int main(void)
{
	// 使用UART口进行日志输出，需要进行初始化
	uart1_init();
	led_init();
	key_init();
	key_exti_init();
	key_nvid_init();
	// 默认情况下，printf虽然有声明，但是没有定义，会导致卡住
	//printf("hehe");
	uart1_send("main\r\n");
	while (1){
		//delay(1);
	}
}

// 中断函数里面不能延迟，不能阻塞
// 此函数名字来自于tartup_stm32f10x_md.s，15_10代表端口范围是10~15，此次端口使用是PB12
static char flag = 0;
void EXTI15_10_IRQHandler()
{
	// 因为范围是10~15，所以触发中断需要判断是否是12
	if (SET == EXTI_GetITStatus(EXTI_Line12))
	{
		if(0 ==flag){
			uart1_send("get data: 0\r\n");
			led_opr(0);
			flag = 1;
		}else{
			uart1_send("get data: 1\r\n");
			led_opr(1);
			flag = 0;
		}
		// 通过UART来发送数据，说明数据已经进入到了中断
		// 如果使用串口，需要在main函数进行初始化
		
		// 这里一定要清空寄存器状态，否则可以看到EXTI外设的PR寄存器的PR12位时钟位“勾”，导致IO中断函数
		// 也会不断触发，所以函数层面响应完毕后，需要对该位进行清零
		EXTI_ClearITPendingBit(EXTI_Line12);
		//uart1_send("Clear register data!\r\n");
	}
}
